
#ifndef _TTG_UTILS_PARAMETERS_PARAMETER_
#define _TTG_UTILS_PARAMETERS_PARAMETER_
#include "Common/Definitions.h"

#include "Common/Errors.h"
#include "Common/Math.h"
#include "Common/Strings.h"
#include "BasicParameter.h"
#include "TypeSerializer.h"
#include "ParameterSet.h"

namespace ttg
{
	namespace utils
	{
		//Represents parameter, that can be modified from external utilities.
		template <class T>
		class Parameter :public ttg_internal::utils::BasicParameter
		{
			private:
				T value;
				//Tries to get actual value and parse it.
				//Does not update "value".
				inline T getActualValue()
				{
					throw ttg::Error(ttg::Error::FATAL, "Parameter<>", "getActualValue",
									 "Parameters of this type does not supported");
				}
				//Tries to cast required value to string and set it as actual value.
				//Does not update "value".
				inline void setActualValue(const T &value)
				{
					throw ttg::Error(ttg::Error::FATAL, "Parameter<>", "getActualValue",
									 "Parameters of this type does not supported");
				}
				//Skips current value to default.
				//Updates "value".
				inline void setDefaultValue()
				{
					this->value = (T)0;
					this->setActualValue(this->value);
				}
				//Tries to serialize type of parameter.
				static inline std::string getActualType()
				{ return ttg_internal::utils::ypeSerializer::serializeVoid(); }
			protected:
				//Creates parameter witd default value. For specific extensions.
				inline Parameter(bool stub, const char *name, const char *type)
					:BasicParameter(name, type)
				{ setDefaultValue(); }
				//Creates parameter with default value and required owner. For specific extensions.
				inline Parameter(bool stub, const char *name, const char *type, ParameterSet *owner)
					:BasicParameter(name, type, owner)
				{ setDefaultValue(); }
			public:
				//Creates parameter witd default value.
				inline Parameter(const char *name = "Unnamed")
					:BasicParameter(name, getActualType().c_str())
				{ setDefaultValue(); }
				//Creates parameter with default value in the required sub-set.
				inline Parameter(const char *name, ParameterSet *owner)
					:BasicParameter(name, getActualType().c_str(), owner)
				{ setDefaultValue(); }
				//Creates parameter with required value.
				inline Parameter(const char *name, const T &value)
					:BasicParameter(name, getActualType().c_str())
				{
					this->value = value;
					setActualValue(this->value);
				}
				//Creates parameter with required value in the required sub-set.
				inline Parameter(const char *name, ParameterSet *owner, const T &value)
					:BasicParameter(name, getActualType().c_str(), owner)
				{
					this->value = value;
					setActualValue(this->value);
				}
				//Locks parameter to avoid its modification (from external utilities).
				inline void lock()
				{ lock_hidden(); }
				//Unlocks parameters and sets its actual value.
				inline void unLock()
				{ unLock_hidden(); }
				//Assigns new value.
				const T &operator =(const T &value)
				{
					this->value = value;
					setActualValue(value);
					return value;
				}
				//Increments current value.
				const T &operator +=(const T &value)
				{
					this->value += value;
					setActualValue(this->value);
					return this->value;
				}
				//Decrements current value.
				const T &operator -=(const T &value)
				{
					this->value -= value;
					setActualValue(this->value);
					return this->value;
				}
				//Multiplies current value.
				const T &operator *=(const T &value)
				{
					this->value *= value;
					setActualValue(this->value);
					return this->value;
				}
				//Divides current value.
				const T &operator /=(const T &value)
				{
					this->value /= value;
					setActualValue(this->value);
					return this->value;
				}
				//Casts parameter to its value.
				inline operator const T()
				{
					if (wasModified())
					{ value = getActualValue(); }
					return value;
				}
		};
		//int32 version
		template <>
		inline ttg::math::int32 Parameter<ttg::math::int32>::getActualValue()
		{
			int res;
			if (ttg::strings::isInt(get(), res))
				return res;
			else
				return 0;
		}
		template <>
		inline void Parameter<ttg::math::int32>::setActualValue(const ttg::math::int32 &value)
		{
			char buf[128];
			sprintf_s(buf, 128, "%d", value);
			set(std::string(buf));
		}
		template <>
		static inline std::string Parameter<ttg::math::int32>::getActualType()
		{ return ttg_internal::utils::TypeSerializer::serializeInt64(); }
		//double version
		template <>
		inline double Parameter<double>::getActualValue()
		{
			double res;
			if (ttg::strings::isFloat(get(), res))
				return res;
			else
				return 0.0;
		}
		template <>
		inline void Parameter<double>::setActualValue(const double &value)
		{
			char buf[128];
			sprintf_s(buf, 128, "%lf", value);
			set(std::string(buf));
		}
		template <>
		static inline std::string Parameter<double>::getActualType()
		{ return ttg_internal::utils::TypeSerializer::serializeDouble(); }
		//std::string version
		template <>
		inline std::string Parameter<std::string>::getActualValue()
		{ return get(); }
		template <>
		inline void Parameter<std::string>::setActualValue(const std::string &value)
		{ set(value); }
		template <>
		inline void Parameter<std::string>::setDefaultValue()
		{ setActualValue(this->value = ""); }
		template <>
		static inline std::string Parameter<std::string>::getActualType()
		{ return ttg_internal::utils::TypeSerializer::serializeString(); }
		//bool version
		template <>
		inline bool Parameter<bool>::getActualValue()
		{
			std::string &v = get();
			return v == "true";
		}
		template <>
		inline void Parameter<bool>::setActualValue(const bool &value)
		{ set(value ? std::string("true") : std::string("false")); }
		template <>
		static inline std::string Parameter<bool>::getActualType()
		{ return ttg_internal::utils::TypeSerializer::serializeBool(); }
	}
}

#endif
